# This file is part of Gehyra.
#
# Gehyra is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gehyra is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gehyra.  If not, see <http://www.gnu.org/licenses/>.


"""@package gehyra.bootstrap.modules.pull_dns
Bootstrap DNS TXT record reader.
Reads a DNS TXT record to get the bootstrap information.
$Id: pull_dns.py 450 2011-01-20 14:28:47Z andyhhp@gmail.com $
"""

""" @file pull_dns.py
Bootstrap DNS TXT record reader.
Reads a DNS TXT record to get the bootstrap information
"""

import random
import socket
from twisted.names import client, dns
from twisted.internet.defer import fail
from twisted.python.failure import (Failure, DefaultException)
from zope.interface import implements
from gehyra.common.interfaces import IBootstrapPuller
from gehyra.common.logger import LOG

class pull_dns(object):
    """Bootstrap DNS TXT record reader.
    Reads a DNS TXT record to get the bootstrap information
    """
    implements(IBootstrapPuller)

    def __init__(self, name, **args):
        """Constructor.
        @param name Name of this bootstrap object given in the configuration.
        @param **args Dictionary of arguments
         - hostname - hostname to look up.
         - servers - optional list of dns servers to use.  Defaults to the OpenDNS,
        Google Public DNS and DNS Advantage services.
        """

        ## @brief Name of this bootstrap object
        self.name = name

        if 'hostname' in args:
            ## @brief Hostname to resolve
            self.hostname = args['hostname']
            del args['hostname']
        else:
            self.hostname = None
            LOG.warn("DNS Puller '%s' has no set hostname" % name)

        if 'servers' in args:
            ## Servers to query
            self.servers = args['servers']
            del args['servers']
        else:
            self.servers = ['208.67.220.220', '208.67.222.222', '8.8.8.8',
                       '8.8.4.4', '156.154.70.1', '156.154.71.1']

        for a in args:
            LOG.warn("DNS Puller '%s' has received unrecognised parameter '%s'"
                     % (name, a) )

        ## @brief twisted.names.client.Resolver to attempt the dns resolution
        self.resolver = None


    def initialize(self):
        """Initialize the object"""

        random.shuffle(self.servers)


        if self.hostname is not None:
            self.resolver = client.Resolver(
                servers=[(ip, dns.PORT) for ip in self.servers],
                timeout=(1, 3, 5, 9))
        else:
            return False

        return True


    def collect(self):
        """Attempt to collect the bootstrap information.
        Starts the resolver and parses the returned data.
        @return Defered object
        """

        if self.resolver is None:
            return fail(Failure(DefaultException("No Hostname")))

        resolve_d = self.resolver.query(dns.Query(self.hostname, type=dns.TXT))
        
    
        def success(result):
            """defered success"""
            LOG.debug("in success")
            raw_data = (a.payload.data[0] for a in result[0])
                    
            data = {}
            
            for r in raw_data:
                (k, v) = r.split('=', 1)
                data[k] = v
            
            return data
        
        def error(error):
            """defered success"""
            ex = error.value
            if isinstance(ex, socket.error):
                LOG.warning("DNS Puller Socket Error: %s" % ex)
            return { "error": error }
        
        resolve_d.addCallbacks(success, error)
        return resolve_d

    def desc(self):
        """Short description.
        @return String giving a short description for the logs.
        """
        if self.hostname is not None:
            return "DNS Puller '%s' resolving '%s'" % (self.name, self.hostname)
        else:
            return "DNS Puller '%s' with no hostname" % self.name
